#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

#define ROW 22 //游戏区行数
#define COL 42 //游戏区列数

#define KONG 0 //标记空（什么也没有）
#define WALL 1 //标记墙
#define FOOD 2 //标记食物
#define HEAD 3 //标记蛇头
#define BODY 4 //标记蛇身

#define UP 72 //方向键：上
#define DOWN 80 //方向键：下
#define LEFT 75 //方向键：左
#define RIGHT 77 //方向键：右
#define SPACE 32 //暂停
#define ESC 27 //退出
#define F1 59 //F1键
#define F2 60 //F2键

//蛇头
struct Snake {
    int len; //记录蛇身长度
    int x; //蛇头横坐标
    int y; //蛇头纵坐标
} snake;

//蛇身
struct Body {
    int x; //蛇身横坐标
    int y; //蛇身纵坐标
} body[ROW*COL]; //开辟足以存储蛇身的结构体数组

int face[ROW][COL]; //标记游戏区各个位置的状态
int sleeptime = 200; //蛇移动的时间间隔
int speed = 1; //速度等级
int max, grade; //全局变量
int direction = RIGHT; //初始方向向右

// 函数声明
void HideCursor();
void CursorJump(int x, int y);
void color(int c);
void ReadGrade();
void WriteGrade();
void InitSnake();
void RandFood();
void JudgeFunc(int x, int y);
void DrawSnake(int flag);
void MoveSnake(int x, int y);
void Game();
void printsnake();
void welcometogame();
void createMap();
void scoreandtips();
void initsnake();
void createfood();
void explation();
void speedup();
void speeddown();
void keyboardControl();
void gameover();

// 欢迎界面 - 打印蛇的字符画
void printsnake() {
    color(10); // 绿色
    CursorJump(15, 5);
    printf("  _________         _________");
    CursorJump(15, 6);
    printf(" /         \\       /         \\");
    CursorJump(15, 7);
    printf("/           \\_____/           \\");
    CursorJump(15, 8);
    printf("\\   Welcome to  the  Snake   /");
    CursorJump(15, 9);
    printf(" \\      Game!      _____    /");
    CursorJump(15, 10);
    printf("  \\               /     \\  /");
    CursorJump(15, 11);
    printf("   \\_____________/       \\/");
}

// 欢迎界面 - 菜单选择
void welcometogame() {
    system("cls");
    printsnake();
    
    color(11); // 浅蓝色
    CursorJump(25, 14);
    printf("1. 开始游戏");
    CursorJump(25, 15);
    printf("2. 游戏说明");
    CursorJump(25, 16);
    printf("3. 最高分");
    CursorJump(25, 17);
    printf("4. 退出游戏");
    
    color(14); // 黄色
    CursorJump(15, 19);
    printf("请选择(1-4): ");
    
    char ch = getch();
    switch(ch) {
        case '1':
            system("cls");
            createMap();
            initsnake();
            createfood();
            DrawSnake(1);
            Game();
            break;
        case '2':
            explation();
            break;
        case '3':
            system("cls");
            color(11);
            CursorJump(20, 10);
            printf("历史最高分: %d", max);
            CursorJump(20, 12);
            printf("按任意键返回...");
            getch();
            welcometogame();
            break;
        case '4':
            exit(0);
        default:
            CursorJump(15, 20);
            printf("输入错误，请重新选择!");
            Sleep(500);
            welcometogame();
    }
}

// 创建游戏地图
void createMap() {
    color(6); // 土黄色
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            if (j == 0 || j == COL - 1) {
                face[i][j] = WALL;
                CursorJump(2 * j, i);
                printf("■");
            }
            else if (i == 0 || i == ROW - 1) {
                face[i][j] = WALL;
                CursorJump(2 * j, i);
                printf("■");
            }
            else {
                face[i][j] = KONG;
            }
        }
    }
    scoreandtips();
}

// 绘制得分和小提示
void scoreandtips() {
    color(7); // 白色
    CursorJump(0, ROW);
    printf("当前得分: %d", grade);
    CursorJump(COL, ROW);
    printf("历史最高分: %d", max);
    CursorJump(2 * COL + 10, 5);
    printf("操作说明:");
    CursorJump(2 * COL + 10, 6);
    printf("方向键: 控制蛇的移动方向");
    CursorJump(2 * COL + 10, 7);
    printf("F1: 加速");
    CursorJump(2 * COL + 10, 8);
    printf("F2: 减速");
    CursorJump(2 * COL + 10, 9);
    printf("空格: 暂停游戏");
    CursorJump(2 * COL + 10, 10);
    printf("ESC: 退出游戏");
}

// 初始化蛇
void initsnake() {
    snake.len = 2;
    snake.x = COL / 2;
    snake.y = ROW / 2;
    
    body[0].x = COL / 2 - 1;
    body[0].y = ROW / 2;
    body[1].x = COL / 2 - 2;
    body[1].y = ROW / 2;
    
    face[snake.y][snake.x] = HEAD;
    face[body[0].y][body[0].x] = BODY;
    face[body[1].y][body[1].x] = BODY;
}

// 创建并随机出现食物
void createfood() {
    int i, j;
    do {
        i = rand() % ROW;
        j = rand() % COL;
    } while (face[i][j] != KONG);
    
    face[i][j] = FOOD;
    color(12); // 红色
    CursorJump(2 * j, i);
    printf("●");
}

// 游戏说明界面
void explation() {
    system("cls");
    color(11);
    CursorJump(20, 3);
    printf("游戏说明");
    color(7);
    CursorJump(10, 5);
    printf("1. 使用方向键控制蛇的移动方向");
    CursorJump(10, 6);
    printf("2. 蛇吃到食物后身体会变长，得分增加");
    CursorJump(10, 7);
    printf("3. 蛇撞到墙壁或自己的身体游戏结束");
    CursorJump(10, 8);
    printf("4. 按F1加速，F2减速");
    CursorJump(10, 9);
    printf("5. 按空格暂停游戏");
    CursorJump(10, 10);
    printf("6. 按ESC退出游戏");
    
    CursorJump(10, 15);
    printf("按任意键返回主菜单...");
    getch();
    welcometogame();
}

// 判断蛇是否咬到自己
int biteself() {
    for (int i = 0; i < snake.len; i++) {
        if (snake.x == body[i].x && snake.y == body[i].y) {
            return 1;
        }
    }
    return 0;
}

// 判断蛇是否撞到墙
void cantcrosswall() {
    if (snake.x <= 0 || snake.x >= COL - 1 || snake.y <= 0 || snake.y >= ROW - 1) {
        gameover();
    }
}

// 设置蛇加速前进
void speedup() {
    if (sleeptime >= 50) {
        sleeptime -= 30;
        speed++;
    }
}

// 设置蛇减速前进
void speeddown() {
    if (sleeptime <= 350) {
        sleeptime += 30;
        speed--;
    }
}

// 通过键盘按键控制蛇的方向
void keyboardControl() {
    if (kbhit()) {
        int key = getch();
        if (key == 224) { // 方向键
            key = getch();
            switch (key) {
                case UP:
                    if (direction != DOWN) direction = UP;
                    break;
                case DOWN:
                    if (direction != UP) direction = DOWN;
                    break;
                case LEFT:
                    if (direction != RIGHT) direction = LEFT;
                    break;
                case RIGHT:
                    if (direction != LEFT) direction = RIGHT;
                    break;
            }
        }
        else if (key == F1) {
            speedup();
        }
        else if (key == F2) {
            speeddown();
        }
        else if (key == SPACE) {
            CursorJump(0, ROW + 1);
            //printf("游戏暂停，按任意键继续...");
            getch();
            CursorJump(0, ROW + 1);
            printf("                          ");
        }
        else if (key == ESC) {
            gameover();
        }
    }
}

// 随机生成食物
void RandFood() {
    int i, j;
    int count = 0;
    int max_tries = ROW * COL * 2; // 最大尝试次数，避免无限循环
    
    do {
        // 随机生成食物的横纵坐标
        i = rand() % (ROW - 2) + 1; // 1到ROW-2之间，避免生成在墙上
        j = rand() % (COL - 2) + 1; // 1到COL-2之间，避免生成在墙上
        count++;
        
        // 如果尝试次数过多，可能蛇已经占满地图，游戏应该结束
        if (count >= max_tries) {
            // 检查是否真的没有空间了
            int space_available = 0;
            for (int y = 1; y < ROW - 1; y++) {
                for (int x = 1; x < COL - 1; x++) {
                    if (face[y][x] == KONG) {
                        space_available = 1;
                        break;
                    }
                }
                if (space_available) break;
            }
            
            if (!space_available) {
                // 地图已满，游戏胜利
                gameover();
                return;
            }
            else {
                // 继续尝试生成食物
                count = 0;
            }
        }
    } while (face[i][j] != KONG); // 确保生成食物的位置为空
    
    face[i][j] = FOOD; // 将食物位置进行标记
    color(12); // 颜色设置为红色
    CursorJump(2 * j, i); // 光标跳转到生成的随机位置处
    printf("●"); // 打印食物
    
    // 更新小提示区域
    color(7);
    CursorJump(2 * COL + 10, 12);
    //printf("当前速度: %d", speed);
    CursorJump(2 * COL + 10, 13);
    //printf("食物位置: (%2d,%2d)", j, i);
}

// 游戏结束界面
void gameover() {
    system("cls");
    color(12);
    CursorJump(20, 5);
    printf("游戏结束!");
    
    color(7);
    CursorJump(20, 7);
    if (grade > max) {
        printf("创纪录啦！最高分被你刷新啦，真棒！！！");
        max = grade;
        WriteGrade();
    }
    else if (grade == max) {
        printf("与最高记录持平，加油再创佳绩");
    }
    else {
        printf("继续努力吧~你离最高分还差: %d", max - grade);
    }
    
    CursorJump(20, 10);
    printf("1. 重玩");
    CursorJump(20, 11);
    printf("2. 退出游戏");
    
    CursorJump(20, 13);
    printf("请选择(1-2): ");
    
    char ch;
    do {
        ch = getch();
        if (ch == '1') {
            system("cls");
            grade = 0;
            direction = RIGHT;
            sleeptime = 200;
            speed = 1;
            welcometogame();
        }
        else if (ch == '2') {
            exit(0);
        }
        else {
            CursorJump(20, 15);
            printf("选择错误，请重新选择!");
        }
    } while (ch != '1' && ch != '2');
}

// 隐藏光标
void HideCursor() {
    CONSOLE_CURSOR_INFO curInfo;
    curInfo.dwSize = 1;
    curInfo.bVisible = FALSE;
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorInfo(handle, &curInfo);
}

// 光标跳转
void CursorJump(int x, int y) {
    COORD pos;
    pos.X = x;
    pos.Y = y;
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(handle, pos);
}

// 颜色设置
void color(int c) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
}

// 从文件读取最高分
void ReadGrade() {
    FILE* pf = fopen("贪吃蛇最高得分记录.txt", "r");
    if (pf == NULL) {
        pf = fopen("贪吃蛇最高得分记录.txt", "w");
        fwrite(&max, sizeof(int), 1, pf);
    }
    fseek(pf, 0, SEEK_SET);
    fread(&max, sizeof(int), 1, pf);
    fclose(pf);
    pf = NULL;
}

// 更新最高分到文件
void WriteGrade() {
    FILE* pf = fopen("贪吃蛇最高得分记录.txt", "w");
    if (pf == NULL) {
        printf("保存最高得分记录失败\n");
        exit(0);
    }
    fwrite(&max, sizeof(int), 1, pf);
    fclose(pf);
    pf = NULL;
}

// 打印蛇与覆盖蛇
void DrawSnake(int flag) {
    if (flag == 1) {
        color(10);
        CursorJump(2 * snake.x, snake.y);
        printf("■");
        for (int i = 0; i < snake.len; i++) {
            CursorJump(2 * body[i].x, body[i].y);
            printf("□");
        }
    }
    else {
        if (body[snake.len - 1].x != 0) {
            CursorJump(2 * body[snake.len - 1].x, body[snake.len - 1].y);
            printf("  ");
        }
    }
}

// 移动蛇
void MoveSnake(int x, int y) {
    DrawSnake(0);
    face[body[snake.len - 1].y][body[snake.len - 1].x] = KONG;
    face[snake.y][snake.x] = BODY;
    
    for (int i = snake.len - 1; i > 0; i--) {
        body[i].x = body[i - 1].x;
        body[i].y = body[i - 1].y;
    }
    
    body[0].x = snake.x;
    body[0].y = snake.y;
    snake.x += x;
    snake.y += y;
    
    DrawSnake(1);
}

// 判断得分与结束
void JudgeFunc(int x, int y) {
    if (face[snake.y + y][snake.x + x] == FOOD) {
        snake.len++;
        grade += 10;
        color(7);
        CursorJump(0, ROW);
        printf("当前得分: %d", grade);
        RandFood();
    }
    else if (face[snake.y + y][snake.x + x] == WALL || biteself()) {
        gameover();
    }
}

// 游戏主体逻辑函数
void Game() {
    while (1) {
        keyboardControl();
        
        switch (direction) {
            case UP:
                JudgeFunc(0, -1);
                MoveSnake(0, -1);
                break;
            case DOWN:
                JudgeFunc(0, 1);
                MoveSnake(0, 1);
                break;
            case LEFT:
                JudgeFunc(-1, 0);
                MoveSnake(-1, 0);
                break;
            case RIGHT:
                JudgeFunc(1, 0);
                MoveSnake(1, 0);
                break;
        }
        
        cantcrosswall();
        Sleep(sleeptime);
    }
}

int main() {
    system("title 贪吃蛇");
    system("mode con cols=84 lines=23");
    HideCursor();
    ReadGrade();
    welcometogame();
    return 0;
}